리액트 프로젝트 - 03 (1) javascript

매력적인 언어 - javascript

javascript는 옛날에는 프로그래머들이 선호하지도 않고 딱히 개발언어라고 생각하지 않았던거 같다.(나의 느낌상) 하지만 웹 시장이 커지고 javascript가 개선되면서 가장 핫한 컴퓨터 언어로 떠올랐다. 사실 이런 이야기는 중요하지 않다.

2015년 기준으로 ECMA가 ES6를 채택하면서 많은 변화가 있었다. (내년마다 새로운 버전이 나오기때문에 ES6도 상당히 오래된 버전이다. 2019년 기준으로 ES11이 나온 상태이다.) 이후 변화들은 정리중이니 정리가 끝나면 포스팅할 예정이다.

리액트 프로젝트 책에서는 ES6에 대해서 다루고 있는데 변수를 정의에 대한 새로운 방법 ,객체와 배열의 사용성 개선, 강화된 함수 기능, 비동기 프로그래밍 - 프로미스, async await, 템플릿 리터럴로 동적인 문자열 생성, 실행을 멈출 수 있는 제너레이터

위 내용을 핵심만 간단간단하게 잡고 넘어갈까 한다.

1.변수를 정의하는 새로운 방법

ES6 이전까지는 var를 이용해서 변수를 정의했고 유일한 방법이었다. 하지만 ES6부터는 const, let을 사용할 수 있게 되었고 const, let이 나온 이유는 var가 많은 문제들이 있기 때문이다.

var의 문제점

(1)문제점 - 함수 스코프

var는 기본적으로 함수 스코프(scope)다. 함수 스코프란 말은 함수 단위의 범위로 정의된다는 것이다. 그래서 함수 내부에서 정의 한다면 함수 밖에서는 참조 에러가 발생한다.

var 변수를 함수가 아닌 가장 바깥에 정의하면 전역 변수가 되는데, 프로그램 전체를 감싸는 하나의 함수가 있다고 생각하면 이해가 되고, 이 방법은 좀 위험한 방법이다. 추천하지 않는다. var 키워드 없이 변수에 값을 할당하면 그 변수는 전역 변수가 된다.

function ex1() {
  var i = 2;
}
console.log(i) // 참조 에러

function ex2() {
  i = 3; //var 키워드 없어서 전역변수
}
function ex3() {
  console.log(i);
}
ex2();
ex3(); // 3 출력

위 상황을 대처하기 위해서 strict 모드를 사용하는데 사용방법은 파일 상단에 “use strict”; 를 작성하면 된다. strict 선언 후에 var 키워드 없이 변수 선언 같은 경우는 에러가 발생한다.

var는 함수 스코프이기 때문에 for문, while문, switch문, if문 등 안에서 사용된 변수도 계속 남게 된다.(보통 for문 할때 var i = 0; 으로 많이 시작하게 된다.)

(2)문제점 - 호이스팅

var로 정의된 변수는 그 변수가 속한 스코프의 최상단으로 끌어올려진다. 이를 호이스팅(hoisting)이라고 한다.(const,let 호이스팅 설명시 더 자세하게 호이스팅을 설명)

// (1)변수 정의 시점보다 먼저 변수 사용
console.log(myVal); //undefined
var myVar = 1;

// (2)호이스팅의 결과
var myVar = undefined;
console.log(myVal); //undefined
myVar = 1;

// (3)정의된 시점보다 먼저 변수에 값을 할당
console.log(myVar); //undefined
myVar = 2;
console.log(myVar); // 2
var myVar = 1;

(1)의 경우 변수를 정의 하기전에 사용 했지만 에러가 발생되기보단 잘 실행되며 1이 아닌 undefined가 출력된다. 호이스팅으로 변수의 정의가 위쪽으로 끌어올려졌다고 보면 된다.

(2)을 보면 이해하기 쉽다. 변수의 정의만 끌어올려지고 값은 원래 정의 했던 위치에서 할당된다.

(3)은 변수가 정의된 곳 위에서 값을 할당할 수 있다 는걸 보여준다.

위 (1)~(3)의 경우 처럼 호이스팅은 직관적이지 않으며, 보통의 프로그래밍 언어에서는 찾아보기 힘든 경우다.

(3)문제점 - 기타

다른 문제로는 var를 이용하면 한 번 정의된 변수를 재정의할 수 있다.

변수를 정의한다는 것은 이전에 없던 변수를 생성한다는 의미로 통영되며, 변수의 제정의가 에러 없이 사용될 수 있다는 것은 직관적이지 않아 버그로 이어질 수 있다.

var는 무조건 재할당 가능한 변수로만 만들 수 있다는 것이다. 재할당이 불가능한 변수를 사용한다면 코드의 복잡도가 낮아지고 가독성이 높아진다.

var의 문제점을 해결할 수 있는 const, let

const, let은 블록 스코프다.

var의 함수 스코프의 단점은 대부분 블록 스코프에는 찾아 볼 수있다. 많은 프로그램 언어에서 블록 스코프를 사용하기 때문에 다른 언어를 사용하는 사람이라면 블록 스코프가 익숙할 것이다.

if (true) {
    const o = 1;
}
console.log(o); // 참조 에러

if문의 블록 안에서 정의된 변수는 블록을 벗어나면 참조할 수 없다. 에러가 발생하는 경우가 더 직관적이며 오히려 이해하기 쉽다. var의 경우에 계속 존재하기 때문에 신경써야하고, 나중에 잘못된 부분이 발생 하더라도 파악하기 쉽지 않다.

const, let에서의 호이스팅

const와 let는 변수가 정의된 위치와 호이스팅된 위치 사이에서 변수를 사용하면 에러가 발생한다.

console.log(foo); // undefined
var foo;

console.log(bar); // Error: Uncaught ReferenceError: bar is not defined
let bar;

변수가 어떻게 생성되고, 호이스팅은 어떻게 이뤄지는가? 변수는 3단계에 걸쳐서 생성된다.

  1. 선언단계

    • 변수를 실행 컨텍스트의 변수 객체에 등록한다. 이 변수 객체는 스코프가 참조하는 대상이 된다.
  2. 초기화 단계

    • 변수 객체에 등록된 변수를 위한 공간을 메모리에 확보한다. 이 단계에서 변수는 undefined로 초기화한다.
  3. 할당 단계

    • (undefined)초기화된 변수에 실제 값을 할당한다.

var변수 같은 경우에는 선언 단계, 초기화 단계가 한번에 이루어진다.

스코프에 변수를 등록하고(선언 단계) 메모리에 변수를 위한 공간을 확보한 후, undefined로 초기화(초기화 단계)한다. 따라서 변수 선언문 이전에 변수에 접근하더라도 스코프에 존재하기 때문에 에러가 발생하지 않고 undefined를 반환한다. 이후 할당 단계를 거치면 값이 할당 된다. 이러한 현상을 변수 호이스팅이라고한다.

하지만 Let으로 선언된 변수는 선언단계, 초기화 단계가 분리되어서 진행된다. 스코프에 변수를 등록 하지만 초기단계는 변수에 도달했을 때 이뤄진다 그래서 초기화 이전에 변수에 접근하려 하면 참조 에러(Reference Error)가 발생한다. 변수가 아직 초기화되지 않았기 때문이다. 선언 단계부터 초기화 단계사이의 구간임시적 사각지대 또는 일시적 사각지대(Temporal Dead Zone; TDZ)라고 부른다.

const는 변수를 재할당이 불가능하다

const 변수는 재할당이 불가능하다 하지만 let,var 변수는 재할당이 가능하다. 재할당 변수는 프로그램의 복잡도를 낮춰주기 때문에 되도록이면 const 변수를 사용하는게 좋다.

const bar = "a";
bar = "b"; //에러
var foo = "a";
foo = "b"; // "b"
let val = "a";
val = "b"; // "b"

위 예제처럼 변수의 값을 재할당하면 에러가 발생하지만 const로 정의된 객체의 내부 속성값은 수정이 가능하다 이미 존재하는 속성값을 수정하거나 새로운 속성값을 추가하는 것 모두 가능하다. 외부 패키지(immer, immutable.js)를 활동하면 객체의 내부 속성값도 수정이 불가능하다. 외부 패키지는 객체를 수정하려고 할 때 기존 객체는 변경하지 않고 새로운 객체를 생성하는 방식이다. 만약 수정만 차단하고 싶다면 자바스크립트 내장 함수를 이용하자

  • Object.preventExtensions
  • Object.seal
  • Object.freeze